#pragma once

#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
// #include "LockGuard.hpp"

using namespace std;

bool gIsSave = false;
const std::string logname = "log.txt";

#define LOG(level, format, ...)                                                \
    do                                                                         \
    {                                                                          \
        LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); \
    } while (0)
#define EnableFile()    \
    do                  \
    {                   \
        gIsSave = true; \
    } while (0)

#define EnableFile()    \
    do                  \
    {                   \
        gIsSave = true; \
    } while (0)

// 1.日志的级别
enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

void SaveFile(const string &filename, const string &message)
{
    ofstream out(filename, ios::app);
    if (!out.is_open())
    {
        return;
    }
    out << message;
    out.close();
}

string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "debug";
        break;
    case INFO:
        return "info";
        break;
    case WARNING:
        return "warning";
        break;
    case ERROR:
        return "error";
        break;
    case FATAL:
        return "fatal";
        break;

    default:
        return "unkown";
        break;
    }
}

string GetTimeString()
{
    time_t cur_time = time(nullptr);
    struct tm *format_time = localtime(&cur_time);
    if (format_time == nullptr)
    {
        return "none";
    }
    char time_buffer[1024];
    snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d,%d-%d-%d",
             format_time->tm_year + 1900,
             format_time->tm_mon + 1,
             format_time->tm_mday,
             format_time->tm_hour,
             format_time->tm_min,
             format_time->tm_sec);
    return time_buffer;
}

// 2.日志是有格式的
// 日志级别 时间 代码所在的文件名/行数 日志的内容
void LogMessage(string filename, int line, bool issave, int level, const char *format, ...)
// void LogMessage(string filename, int line, int level, const char *format, ...)
{
    string levelstr = LevelToString(level);
    string timestr = GetTimeString();
    pid_t selfid = getpid();

    char buffer[1024];
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, sizeof(buffer), format, arg); // 根据format将可变参数列表arg进行格式化，存储到buffer
    va_end(arg);

    string message = "[" + timestr + "]" + "[" + levelstr + "]" + "[" + to_string(selfid) + "]" + "[" + filename + "]" + "[" + to_string(line) + "]" + buffer + "\n";

    // cout << message;

    if (!issave)
    {
        cout << message;
    }
    else
    {
        SaveFile(logname, message);
    }
}

// 可变参数：1.int num是一个固定参数，它指定了函数至少需要接受一个int类型的参数
// 2. ...时可变参数列表：意味着在num之后函数可以接收任意数量和类型的额外参数
// void Test(int num, ...)
// {
//     va_list arg;
//     va_start(arg, num);//va_start 是一个宏，用于初始化 va_list 变量 arg，使其指向可变参数列表的起始位置
//     //第二个参数 num 是可变参数列表之前的最后一个固定参数，va_start 需要借助这个参数来确定可变参数的起始位置。

//     while (num)
//     {
//         int data = va_arg(arg, int);
//         cout << "data:" << data << endl;
//         num--;
//     }

//     va_end(arg);
// }